package com.kexilo.core.common.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 * 
 * @author Kexilo
 */
public class JwtUtils {

    private static final String SECRET_KEY = "kexilo-jwt-secret-key-for-auth-system-2024";
    private static final long EXPIRATION_TIME = 24 * 60 * 60 * 1000; // 24小时
    private static final String TOKEN_PREFIX = "Bearer ";
    private static final String HEADER_STRING = "Authorization";

    /**
     * 生成JWT token
     * 
     * @param claims 载荷
     * @return token
     */
    public static String createToken(Map<String, Object> claims) {
        return createToken(claims, EXPIRATION_TIME);
    }

    /**
     * 生成JWT token
     * 
     * @param claims 载荷
     * @param expiration 过期时间（毫秒）
     * @return token
     */
    public static String createToken(Map<String, Object> claims, long expiration) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 根据用户信息生成token
     * 
     * @param userId 用户ID
     * @param username 用户名
     * @return token
     */
    public static String createToken(Long userId, String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);
        claims.put("jti", generateJti());
        return createToken(claims);
    }

    /**
     * 从token中获取Claims
     * 
     * @param token JWT token
     * @return Claims
     */
    public static Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(getSigningKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 从token中获取用户名
     * 
     * @param token JWT token
     * @return 用户名
     */
    public static String getUsernameFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.get("username", String.class) : null;
    }

    /**
     * 从token中获取用户ID
     * 
     * @param token JWT token
     * @return 用户ID
     */
    public static Long getUserIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        if (claims != null) {
            Object userId = claims.get("userId");
            if (userId instanceof Integer) {
                return ((Integer) userId).longValue();
            } else if (userId instanceof Long) {
                return (Long) userId;
            }
        }
        return null;
    }

    /**
     * 从token中获取JTI
     * 
     * @param token JWT token
     * @return JTI
     */
    public static String getJtiFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.get("jti", String.class) : null;
    }

    /**
     * 获取token的过期时间
     * 
     * @param token JWT token
     * @return 过期时间
     */
    public static Date getExpirationDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.getExpiration() : null;
    }

    /**
     * 判断token是否过期
     * 
     * @param token JWT token
     * @return true: 过期, false: 未过期
     */
    public static boolean isTokenExpired(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration != null && expiration.before(new Date());
    }

    /**
     * 验证token
     * 
     * @param token JWT token
     * @return true: 有效, false: 无效
     */
    public static boolean validateToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            return claims != null && !isTokenExpired(token);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 刷新token
     * 
     * @param token 原token
     * @return 新token
     */
    public static String refreshToken(String token) {
        Claims claims = getClaimsFromToken(token);
        if (claims == null) {
            return null;
        }
        
        // 创建新的claims，保持原有信息
        Map<String, Object> newClaims = new HashMap<>();
        newClaims.put("userId", claims.get("userId"));
        newClaims.put("username", claims.get("username"));
        newClaims.put("jti", generateJti()); // 生成新的JTI
        
        return createToken(newClaims);
    }

    /**
     * 从请求头中获取token
     * 
     * @param authHeader Authorization header
     * @return token
     */
    public static String getTokenFromHeader(String authHeader) {
        if (StringUtils.isNotEmpty(authHeader) && authHeader.startsWith(TOKEN_PREFIX)) {
            return authHeader.substring(TOKEN_PREFIX.length());
        }
        return null;
    }

    /**
     * 获取签名密钥
     * 
     * @return SecretKey
     */
    private static SecretKey getSigningKey() {
        byte[] keyBytes = SECRET_KEY.getBytes();
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 生成JTI (JWT ID)
     * 
     * @return JTI
     */
    private static String generateJti() {
        return StringUtils.substring(java.util.UUID.randomUUID().toString().replace("-", ""), 0, 16);
    }

    /**
     * 获取token剩余有效时间（秒）
     * 
     * @param token JWT token
     * @return 剩余有效时间（秒），如果token无效返回0
     */
    public static long getTimeToExpiration(String token) {
        Date expiration = getExpirationDateFromToken(token);
        if (expiration != null) {
            long remainingTime = expiration.getTime() - System.currentTimeMillis();
            return Math.max(0, remainingTime / 1000);
        }
        return 0;
    }

    /**
     * 判断token是否需要刷新（还有30分钟过期）
     * 
     * @param token JWT token
     * @return true: 需要刷新, false: 不需要刷新
     */
    public static boolean shouldRefresh(String token) {
        long timeToExpiration = getTimeToExpiration(token);
        return timeToExpiration > 0 && timeToExpiration < 30 * 60; // 30分钟
    }
}
